/**
 * 
 */
package com.xmlmod.tasks;

import java.io.File;
import java.io.IOException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.xmlmod.util.LoadDocument;
import com.xmlmod.util.XMLModResource;

/**
 * @author W. Cowger
 */
public class XMLMod extends Task{
	private String srcfile = "";
	private String destfile = "";
	private Document doc = null;
	protected int logLevel = 3; // 0 = Display all output, 3 = Debug only, 4 = Warning and above
	private boolean docLoaded = false;
	private XMLModResource resource;
	
	public XMLMod(){
		this.log("Initializing XMLMod Task");
		resource = XMLModResource.getResource("com.xmlmod.util.resource.XMLModResource");
	}
	
	/**
	 * Sets the source file
	 * 
	 * @param src -The absolute path of the source file
	 */
	public void setSrcfile(String srcfile){
		this.srcfile = srcfile;		
	}
	
	/**
	 * Sets the destination file
	 * 
	 * @param dest -The absolute path of the destination file
	 */
	public void setDestfile(String destfile){
		this.destfile = destfile;
	}
	
	public void setVerbose(String verbose){
		if("true".equalsIgnoreCase(verbose)){
			logLevel = 2; // Display output flagged as verbose
		}else if("DEBUG".equalsIgnoreCase(verbose)){
			logLevel = 0; // Display ALL output
		}else{
			logLevel = 3; // default, no minor output unless warning or above
		}
	}
	
	public void addConfiguredRemove(Remove remove){
		remove.process(this);
	}
	
	/**
	 * Called by Ant to execute the task
	 */
	public void execute()throws BuildException{
		if(docLoaded){
			this.log(resource.getMessage("XMLMOD_ACTION_PERFORMED"), logLevel);
			//outputXml(doc, 0);
			doc.normalizeDocument();
			writeDocumentToFile(doc);
		}else{
			this.log(resource.getMessage("XMLMOD_NO_ACTION_PERFORMED"));
		}
		
	}
	
	protected Document loadDocument(){
		if(!docLoaded){
			this.log(resource.getMessage("XMLMOD_DOC_LOADING"), logLevel);
			try {
				this.log("XMLMod Log Level: " + logLevel);
				if(validateParams()){
					// Get the document, make sure it loads into the DOM parser
					doc = LoadDocument.loadXMLDocument(srcfile);
					doc.getDocumentElement().normalize();
					
					this.log("Document Loaded!", logLevel);
					this.log(doc.getDocumentElement().getNodeName(), logLevel);
					
					//outputXml(doc, 0);
				}else{
					throw new BuildException(resource.getMessage("XMLMOD_INVALID_PARAMS"));
				}
			} catch (ParserConfigurationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (SAXException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			//Set the flag to avoid re-loading the doc in future tasks
			docLoaded = true;
		}else{
			this.log(resource.getMessage("XMLMOD_DOC_LOADED"), logLevel);
		}
		
		return doc;
	}
	
	/**
	 * Checks each parameter of the input, returning the first failed one or a true
	 * 
	 * @return true if all params are present, false otherwise
	 */
	private boolean validateParams(){
		boolean valid = true;
		
		if(srcfile == null || "".equals(srcfile)){
			valid = false;
			this.log("Parameter 'srcfile' must be supplied");
		}
		if(destfile == null || "".equals(destfile)){
			valid = false;
			this.log("Parameter 'destfile' must be supplied");
		}
		
		return valid;
	}
	
	/**
	 * Returns the log level of this task for children's reference
	 * 
	 * @return -The log level of this task
	 */
	public int getLogLevel(){
		return this.logLevel;
	}
	
	protected void setDocument(Document doc){
		this.doc = doc;
	}
	
	@SuppressWarnings("unused")
	private void outputXml(Node node, int tabCnt){
		NodeList children = node.getChildNodes();
		NamedNodeMap attrs = node.getAttributes();
		String tabs = "";
		
		for(int i=0; i<tabCnt; i++){
			tabs += "\t";
		}
		short nodeType = node.getNodeType();
		if (nodeType == Node.DOCUMENT_NODE || nodeType == Node.DOCUMENT_TYPE_NODE || nodeType == Node.ELEMENT_NODE){
			System.out.print(tabs + node.getNodeName());
			System.out.print(" " +  node.getTextContent());
			System.out.print("\n");
		}
		
		if(children != null){
			for(int k=0; k<children.getLength(); k++){
				short type = children.item(k).getNodeType(); 
				if(type == Node.DOCUMENT_NODE || type == Node.DOCUMENT_TYPE_NODE || type == Node.ELEMENT_NODE){
					outputXml(children.item(k), tabCnt + 1);
				}
			}
		}
	}
	
	private void writeDocumentToFile(Document doc){
		try{
			Source input = new DOMSource(doc);
			File file = new File(destfile);
			Result output = new StreamResult(file.toURI().getPath());
			Transformer transformer = TransformerFactory.newInstance().newTransformer();
			
			transformer.transform(input, output);
		}catch (TransformerConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}catch (TransformerFactoryConfigurationError e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}catch (TransformerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
